594 lines
23 KiB
Plaintext
594 lines
23 KiB
Plaintext
page.title=Manage Your App's Memory
|
||
page.tags=ram,low memory,OutOfMemoryError,onTrimMemory
|
||
|
||
@jd:body
|
||
|
||
<div id="qv-wrapper">
|
||
<div id="qv">
|
||
|
||
<h2>In this document</h2>
|
||
<ol>
|
||
<li><a href="#monitor">Monitor Available Memory and Memory Usage</a>
|
||
<ul>
|
||
<li><a href="#AnalyzeRam">Tools for analyzing RAM usage</a></li>
|
||
<li><a href="#release">Release memory in response to events</a></li>
|
||
<li><a href="#CheckHowMuchMemory">Check how much memory you should use</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#code">Use More Efficient Code Constructs</a>
|
||
<ul>
|
||
<li><a href="#Services">Use services sparingly</a></li>
|
||
<li><a href="#DataContainers">Use optimized data containers</a></li>
|
||
<li><a href="#Abstractions">Be careful with code abstractions</a></li>
|
||
<li><a href="#NanoProto">Use nano protobufs for serialized data</a></li>
|
||
<li><a href="#churn">Avoid memory churn</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#remove">Remove Memory-Intensive Resources and Libraries</a>
|
||
<ul>
|
||
<li><a href="#reduce">Reduce overall APK size</a></li>
|
||
<li><a href="#DependencyInjection">Avoid dependency injection frameworks</a></li>
|
||
<li><a href="#ExternalLibs">Be careful about using external libraries</a></li>
|
||
</ul>
|
||
</li>
|
||
</ol>
|
||
<h2>See Also</h2>
|
||
<ul>
|
||
<li><a href="{@docRoot}training/articles/memory-overview.html">Overview of Android Memory Management</a>
|
||
</li>
|
||
<li><a href="{@docRoot}studio/profile/investigate-ram.html">Investigating Your RAM Usage</a>
|
||
</li>
|
||
<li><a href="{@docRoot}topic/performance/reduce-apk-size.html">Reduce APK Size</a></li>
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- INTRO #################################################### -->
|
||
|
||
<p>
|
||
Random-access memory (RAM) is a valuable
|
||
resource in any software development environment, but
|
||
it's even more valuable on a mobile operating system
|
||
where physical memory is often constrained.
|
||
Although both the Android Runtime (ART) and Dalvik virtual machine perform
|
||
routine garbage collection, this does not mean you can ignore
|
||
when and where your app allocates and releases memory.
|
||
You still need to avoid
|
||
introducing memory leaks, usually caused by holding onto
|
||
object references in static member variables, and
|
||
release any {@link java.lang.ref.Reference} objects at the appropriate
|
||
time as defined by
|
||
lifecycle callbacks.
|
||
</p>
|
||
|
||
<p>
|
||
This page explains how you can
|
||
proactively reduce memory usage within your app.
|
||
For more information about general
|
||
practices to clean up your resources when programming in Java,
|
||
refer to other books or online
|
||
documentation about managing resource references.
|
||
If you’re looking for information about how to
|
||
analyze memory in a running app, read
|
||
<a href="#AnalyzeRam">Tools for analyzing RAM usage</a>.
|
||
For more detailed information about how the Android Runtime and Dalvik
|
||
virtual machine manage memory, see the
|
||
<a href="{@docRoot}training/articles/memory-overview.html">Overview of Android Memory Management</a>.
|
||
</p>
|
||
|
||
<!-- Section 1 #################################################### -->
|
||
|
||
<h2 id="monitor">Monitor Available Memory and Memory Usage</h2>
|
||
|
||
<p>
|
||
The Android framework, Android Studio, and Android SDK
|
||
can help you analyze and adjust your app's memory usage.
|
||
The Android framework
|
||
exposes several APIs that allow your app to reduce its memory usage
|
||
dynamically during runtime. Android Studio and the Android SDK
|
||
contain several tools that allow you to investigate how your
|
||
app uses memory.
|
||
</p>
|
||
|
||
<!-- Section 1.1 #################################################### -->
|
||
|
||
<h3 id="AnalyzeRam">Tools for analyzing RAM usage</h3>
|
||
|
||
<p>
|
||
Before you can fix the memory usage problems in your app, you first need
|
||
to find them. Android Studio and the Android SDK include several tools
|
||
for analyzing memory usage in your app:
|
||
</p>
|
||
|
||
<ol>
|
||
<li>
|
||
The Device Monitor has a Dalvik Debug Monitor Server (DDMS) tool that allows
|
||
you to inspect memory allocation within your app process.
|
||
You can use this information to understand how your
|
||
app uses memory overall. For example, you can force a garbage collection
|
||
event and then view the types of objects that remain in memory. You can
|
||
use this information to identify operations or actions within your app
|
||
that allocate or leave excessive amounts of objects in memory.
|
||
|
||
<p>For more information about how to use the DDMS tool, see
|
||
<a href="/studio/profile/ddms.html">Using DDMS</a>.
|
||
</p>
|
||
</li>
|
||
|
||
<li>
|
||
The Memory Monitor in Android Studio shows you how your app allocates
|
||
memory over the course of a single session.
|
||
The tool shows a graph of available
|
||
and allocated Java memory over time, including garbage collection events.
|
||
You can also initiate garbage collection events and take a snapshot of
|
||
the Java heap while your app runs. The output from the Memory Monitor tool
|
||
can help you identify points when your app experiences excessive garbage
|
||
collection events, leading to app slowness.
|
||
<p>
|
||
For more information about how to use Memory Monitor tool, see
|
||
<a href="{@docRoot}tools/debugging/debugging-memory.html#ViewHeap">Viewing Heap Updates</a>.
|
||
</p>
|
||
</li>
|
||
|
||
<li>
|
||
Garbage collection events also show up in the Traceview viewer. Traceview
|
||
allows you to view trace log files as both a timeline and as a profile
|
||
of what happened within a method. You can use this tool to determine
|
||
what code was executing when a garbage collection event occurred.
|
||
<p>
|
||
For more information about how to use the Traceview viewer, see
|
||
<a href="https://developer.android.com/studio/profile/traceview.html">Profiling with Traceview and dmtracedump</a>.
|
||
</p>
|
||
</li>
|
||
|
||
<li>
|
||
The Allocation Tracker tool in Android Studio gives you a detailed look
|
||
at how your app allocates memory.
|
||
The Allocation Tracker records an app's memory allocations and lists
|
||
all allocated objects within the profiling snapshot. You can use this
|
||
tool to track down parts of your code that allocate too many objects.
|
||
|
||
<p>
|
||
For more information about how to use the Allocation Tracker tool, see
|
||
<a href="{docRoot}studio/profile/allocation-tracker-walkthru.html">Allocation Tracker Walkthrough</a>.
|
||
</p>
|
||
</li>
|
||
|
||
</ol>
|
||
|
||
<!-- Section 1.2 #################################################### -->
|
||
|
||
<h3 id="release">Release memory in response to events</h3>
|
||
|
||
<p>
|
||
An Android device can run with varying amounts of free memory
|
||
depending on the physical amount of RAM on the device and how the user
|
||
operates it. The system broadcasts signals to indicate when it is under
|
||
memory pressure, and apps should listen for these signals and adjust
|
||
their memory usage as appropriate.
|
||
</p>
|
||
|
||
</p>
|
||
You can use the {@link android.content.ComponentCallbacks2} API
|
||
to listen for these signals and then adjust your memory
|
||
usage in response to app lifecycle
|
||
or device events. The
|
||
{@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}
|
||
method allows your app to listen for memory related events when the app runs
|
||
in the foreground (is visible) and when it runs in the background.
|
||
</p>
|
||
|
||
<p>
|
||
To listen for these events, implement the {@link
|
||
android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}
|
||
callback in your {@link android.app.Activity}
|
||
classes, as shown in the following code snippet.
|
||
</p>
|
||
|
||
<pre class="prettyprint">
|
||
import android.content.ComponentCallbacks2;
|
||
// Other import statements ...
|
||
|
||
public class MainActivity extends AppCompatActivity
|
||
implements ComponentCallbacks2 {
|
||
|
||
// Other activity code ...
|
||
|
||
/**
|
||
* Release memory when the UI becomes hidden or when system resources become low.
|
||
* @param level the memory-related event that was raised.
|
||
*/
|
||
public void onTrimMemory(int level) {
|
||
|
||
// Determine which lifecycle or system event was raised.
|
||
switch (level) {
|
||
|
||
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
|
||
|
||
/*
|
||
Release any UI objects that currently hold memory.
|
||
|
||
The user interface has moved to the background.
|
||
*/
|
||
|
||
break;
|
||
|
||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
|
||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
|
||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
|
||
|
||
/*
|
||
Release any memory that your app doesn't need to run.
|
||
|
||
The device is running low on memory while the app is running.
|
||
The event raised indicates the severity of the memory-related event.
|
||
If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
|
||
begin killing background processes.
|
||
*/
|
||
|
||
break;
|
||
|
||
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
|
||
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
|
||
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
|
||
|
||
/*
|
||
Release as much memory as the process can.
|
||
|
||
The app is on the LRU list and the system is running low on memory.
|
||
The event raised indicates where the app sits within the LRU list.
|
||
If the event is TRIM_MEMORY_COMPLETE, the process will be one of
|
||
the first to be terminated.
|
||
*/
|
||
|
||
break;
|
||
|
||
default:
|
||
/*
|
||
Release any non-critical data structures.
|
||
|
||
The app received an unrecognized memory level value
|
||
from the system. Treat this as a generic low-memory message.
|
||
*/
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
</pre>
|
||
|
||
<p>
|
||
The
|
||
{@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}
|
||
callback was added in Android 4.0 (API level 14). For earlier versions,
|
||
you can use the
|
||
{@link android.content.ComponentCallbacks#onLowMemory()}
|
||
callback as a fallback for older versions, which is roughly equivalent to the
|
||
{@link android.content.ComponentCallbacks2#TRIM_MEMORY_COMPLETE} event.
|
||
</p>
|
||
|
||
<!-- Section 1.3 #################################################### -->
|
||
|
||
<h3 id="CheckHowMuchMemory">Check how much memory you should use</h3>
|
||
|
||
<p>
|
||
To allow multiple running processes, Android sets a hard limit
|
||
on the heap size alloted for each app. The exact heap size limit varies
|
||
between devices based on how much RAM the device
|
||
has available overall. If your app has reached the heap capacity and
|
||
tries to allocate more
|
||
memory, the system throws an {@link java.lang.OutOfMemoryError}.
|
||
</p>
|
||
|
||
<p>
|
||
To avoid running out of memory, you can to query the system to determine
|
||
how much heap space you have available on the current device.
|
||
You can query the system for this figure by calling
|
||
{@link android.app.ActivityManager#getMemoryInfo(android.app.ActivityManager.MemoryInfo) getMemoryInfo()}.
|
||
This returns an
|
||
{@link android.app.ActivityManager.MemoryInfo } object that provides
|
||
information about the device's
|
||
current memory status, including available memory, total memory, and
|
||
the memory threshold—the memory level below which the system begins
|
||
to kill processes. The
|
||
{@link android.app.ActivityManager.MemoryInfo } class also exposes a simple
|
||
boolean field,
|
||
{@link android.app.ActivityManager.MemoryInfo#lowMemory }
|
||
that tells you whether the device is running low on memory.
|
||
</p>
|
||
|
||
<p>
|
||
The following code snippet shows an example of how you can use the
|
||
{@link android.app.ActivityManager#getMemoryInfo(android.app.ActivityManager.MemoryInfo) getMemoryInfo()}.
|
||
method in your application.
|
||
</p>
|
||
|
||
<pre class="prettyprint">
|
||
public void doSomethingMemoryIntensive() {
|
||
|
||
// Before doing something that requires a lot of memory,
|
||
// check to see whether the device is in a low memory state.
|
||
ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
|
||
|
||
if (!memoryInfo.lowMemory) {
|
||
// Do memory intensive work ...
|
||
}
|
||
}
|
||
|
||
// Get a MemoryInfo object for the device's current memory status.
|
||
private ActivityManager.MemoryInfo getAvailableMemory() {
|
||
ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
|
||
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
|
||
activityManager.getMemoryInfo(memoryInfo);
|
||
return memoryInfo;
|
||
}
|
||
</pre>
|
||
|
||
<!-- Section 2 #################################################### -->
|
||
|
||
<h2 id="code">Use More Memory-Efficient Code Constructs</h2>
|
||
|
||
<p>
|
||
Some Android features, Java classes, and code constructs tend to
|
||
use more memory than others. You can minimize how
|
||
much memory your app uses by choosing more efficient alternatives in
|
||
your code.
|
||
</p>
|
||
|
||
<!-- Section 2.1 #################################################### -->
|
||
|
||
<h3 id="Services">Use services sparingly</h3>
|
||
|
||
<p>
|
||
Leaving a service running when it’s not needed is
|
||
<strong>one of the worst memory-management
|
||
mistakes</strong> an Android app can make. If your app needs a
|
||
<a href="{@docRoot}guide/components/services.html">service</a>
|
||
to perform work in the background, do not keep it running unless
|
||
it needs to run a job. Remember to stop your service when it has completed
|
||
its task. Otherwise, you can inadvertently cause a memory leak.
|
||
</p>
|
||
|
||
<p>
|
||
When you start a service, the system prefers to always keep the process
|
||
for that service running. This behavior
|
||
makes services processes very expensive
|
||
because the RAM used by a service remains unavailable to other processes.
|
||
This reduces the number of cached processes that the system can keep in
|
||
the LRU cache, making app switching less efficient. It can even lead to
|
||
thrashing in the system when memory is tight and the system can’t
|
||
maintain enough processes to host all the services currently running.
|
||
</p>
|
||
|
||
<p>
|
||
You should generally avoid use of persistent services because of
|
||
the on-going demands they place on available memory. Instead, we
|
||
recommend that you use an alternative implementation
|
||
such as {@link android.app.job.JobScheduler}. For more information about
|
||
how to use {@link android.app.job.JobScheduler} to schedule background
|
||
processes, see
|
||
<a href="/topic/performance/background-optimization.html">Background Optimizations</a>.
|
||
<p>
|
||
If you must use a service, the
|
||
best way to limit the lifespan of your service is to use an {@link
|
||
android.app.IntentService}, which finishes
|
||
itself as soon as it's done handling the intent that started it.
|
||
For more information, read
|
||
<a href="{@docRoot}training/run-background-service/index.html">Running in a Background Service</a>.
|
||
</p>
|
||
|
||
<!-- Section 2.2 #################################################### -->
|
||
|
||
<h3 id="DataContainers">Use optimized data containers</h3>
|
||
|
||
<p>
|
||
Some of the classes provided by the programming language are not optimized for
|
||
use on mobile devices. For example, the generic
|
||
{@link java.util.HashMap} implementation can be quite memory
|
||
inefficient because it needs a separate entry object for every mapping.
|
||
</p>
|
||
|
||
<p>
|
||
The Android framework includes several optimized data containers, including
|
||
{@link android.util.SparseArray}, {@link android.util.SparseBooleanArray},
|
||
and {@link android.support.v4.util.LongSparseArray}.
|
||
For example, the {@link android.util.SparseArray} classes are more
|
||
efficient because they avoid the system's need to
|
||
<acronym title="Automatic conversion from primitive types to object classes (such as int to Integer)">autobox</acronym>
|
||
the key and sometimes value (which creates yet another object or
|
||
two per entry).
|
||
</p>
|
||
|
||
<p>
|
||
If necessary, you can always switch to raw arrays for a really lean data
|
||
structure.
|
||
</p>
|
||
|
||
<!-- Section 2.3 #################################################### -->
|
||
|
||
<h3 id="Abstractions">Be careful with code abstractions</h3>
|
||
|
||
<p>
|
||
Developers often use abstractions simply as a good programming practice,
|
||
because abstractions can improve code flexibility and maintenance.
|
||
However, abstractions come at a significant cost:
|
||
generally they require a fair amount more code that
|
||
needs to be executed, requiring more time and
|
||
more RAM for that code to be mapped into memory.
|
||
So if your abstractions aren't supplying a
|
||
significant benefit, you should avoid them.
|
||
</p>
|
||
|
||
<p>
|
||
For example, enums often require more than twice as much memory as static
|
||
constants. You should strictly avoid using enums on Android.
|
||
</p>
|
||
|
||
<!-- Section 2.4 #################################################### -->
|
||
|
||
<h3 id="NanoProto">Use nano protobufs for serialized data</h3>
|
||
|
||
<p>
|
||
<a href="https://developers.google.com/protocol-buffers/docs/overview">Protocol buffers</a>
|
||
are a language-neutral, platform-neutral, extensible mechanism
|
||
designed by Google for serializing structured data—similar to XML, but
|
||
smaller, faster, and simpler. If you decide to use
|
||
protobufs for your data, you should always use nano protobufs in your
|
||
client-side code. Regular protobufs generate extremely verbose code, which
|
||
can cause many kinds of problems in your app such as
|
||
increased RAM use, significant APK size increase, and slower execution.
|
||
</p>
|
||
|
||
<p>
|
||
For more information, see the "Nano version" section in the
|
||
<a href="https://android.googlesource.com/platform/external/protobuf/+/master/java/README.txt"
|
||
class="external-link">protobuf readme</a>.
|
||
</p>
|
||
|
||
<!-- Section 2.5 #################################################### -->
|
||
|
||
<h3 id="churn">Avoid memory churn</h3>
|
||
|
||
<p>
|
||
As mentioned previously, garbage collections events don't normally affect
|
||
your app's performance. However, many garbage collection events that occur
|
||
over a short period of time can quickly eat up your frame time. The more time
|
||
that the system spends on garbage collection, the less time it has to do
|
||
other stuff like rendering or streaming audio.
|
||
</p>
|
||
|
||
<p>
|
||
Often, <em>memory churn</em> can cause a large number of
|
||
garbage collection events to occur. In practice, memory churn describes the
|
||
number of allocated temporary objects that occur in a given amount of time.
|
||
</p>
|
||
|
||
<p>
|
||
For example, you might allocate multiple temporary objects within a
|
||
<code>for</code> loop. Or you might create new
|
||
{@link android.graphics.Paint} or {@link android.graphics.Bitmap}
|
||
objects inside the
|
||
{@link android.view.View#onDraw(android.graphics.Canvas) onDraw()}
|
||
function of a view.
|
||
In both cases, the app creates a lot of objects quickly at high volume.
|
||
These can quickly consume all the available memory in the young generation,
|
||
forcing a garbage collection event to occur.
|
||
</p>
|
||
|
||
<p>
|
||
Of course, you need to find the places in your code where
|
||
the memory churn is high before you can fix them. Use the tools discussed in
|
||
<a href="#AnalyzeRam">Analyze your RAM usage</a>
|
||
</p>
|
||
|
||
<p>
|
||
Once you identify the problem areas in your code, try to reduce the number of
|
||
allocations within performance critical areas. Consider moving things out of
|
||
inner loops or perhaps moving them into a
|
||
<a href="https://en.wikipedia.org/wiki/Factory_method_pattern" class="external-link">Factory</a>
|
||
based allocation structure.
|
||
</p>
|
||
|
||
<!-- Section 3 #################################################### -->
|
||
|
||
<h2 id="remove">Remove Memory-Intensive Resources and Libraries</h2>
|
||
|
||
<p>
|
||
Some resources and libraries within your code can gobble up memory without
|
||
you knowing it. Overall size of your APK, including third-party libraries
|
||
or embedded resources, can affect how much memory your app consumes. You can
|
||
improve your app's memory consumption by removing any redundant, unnecessary,
|
||
or bloated components, resources, or libraries from your code.
|
||
</p>
|
||
|
||
<!-- Section 3.1 #################################################### -->
|
||
|
||
<h3 id="reduce">Reduce overall APK size</h3>
|
||
|
||
<p>
|
||
You can significantly reduce your app's memory usage by reducing the overall
|
||
size of your app. Bitmap size, resources, animation frames, and third-party
|
||
libraries can all contribute to the size of your APK.
|
||
Android Studio and the Android SDK provide multiple tools
|
||
to help you reduce the size of your resources and external dependencies.
|
||
</p>
|
||
|
||
<p>
|
||
For more information about how to reduce your overall APK size, see
|
||
<a href="{@docRoot}topic/performance/reduce-apk-size.html">Reduce APK Size</a>.
|
||
</p>
|
||
|
||
<!-- Section 3.2 #################################################### -->
|
||
|
||
<h3 id="DependencyInjection">Use caution with dependency injection frameworks</h3>
|
||
|
||
<p>
|
||
Dependency injection framework such as
|
||
<a href="https://code.google.com/p/google-guice/" class="external-link">Guice</a>
|
||
or
|
||
<a href="https://github.com/roboguice/roboguice" class="external-link">RoboGuice</a>
|
||
can simplify the code you write and provide an adaptive environment
|
||
that's useful for testing and other configuration changes. However, dependency
|
||
frameworks aren't always optimized for mobile devices.
|
||
</p>
|
||
|
||
<p>
|
||
For example, these frameworks tend to initialize processes by
|
||
scanning your code for annotations. This which can require significant
|
||
amounts of your code to be mapped into RAM unnecessarily. The system
|
||
allocates these mapped pages into clean memory so Android can drop them; yet
|
||
that can't happen until the pages have remained in memory for a long period
|
||
of time.
|
||
</p>
|
||
|
||
<p>
|
||
If you need to use a dependency injection framework in your app, consider
|
||
using
|
||
<a class="external-link" href="http://google.github.io/dagger/">Dagger</a>
|
||
instead. For example, Dagger does not use reflection to scan your app's code.
|
||
Dagger's strict implementation means that it can be used in Android apps
|
||
without needlessly increasing memory usage.
|
||
</p>
|
||
|
||
<!-- Section 3.3 #################################################### -->
|
||
|
||
<h3 id="ExternalLibs">Be careful about using external libraries</h3>
|
||
|
||
<p>
|
||
External library code is often not written for mobile environments and
|
||
can be inefficient when used
|
||
for work on a mobile client. When you decide to use an
|
||
external library, you may need to optimize that library for mobile devices.
|
||
Plan for that work up-front and analyze the library in terms of code size and
|
||
RAM footprint before deciding to use it at all.
|
||
</p>
|
||
|
||
<p>
|
||
Even some mobile-optimized libraries can cause problems due to differing
|
||
implementations. For example, one library may use nano protobufs
|
||
while another uses micro protobufs, resulting in two different protobuf
|
||
implementations in your app. This can happen with different
|
||
implementations of logging, analytics, image loading frameworks,
|
||
caching, and many other things you don't expect.
|
||
</p>
|
||
|
||
<p>
|
||
Although <a href="{@docRoot}tools/help/proguard.html">ProGuard</a> can
|
||
help to remove APIs and resources with the right flags, it can't remove a
|
||
library's large internal dependencies. The features that you want in these
|
||
libraries may require lower-level dependencies. This becomes especially
|
||
problematic when you use an {@link android.app.Activity } subclass from a
|
||
library (which will tend to have wide swaths of dependencies),
|
||
when libraries use reflection (which is common and means you need to spend a
|
||
lot of time manually tweaking ProGuard to get it to work), and so on.
|
||
</p>
|
||
|
||
<p>
|
||
Also avoid using a shared library for just one or two features out of dozens.
|
||
You don't want to pull in a large amount of code and overhead that
|
||
you don't even use. When you consider whether to use a library, look for
|
||
an implementation that strongly matches what you need. Otherwise, you might
|
||
decide to create your own implementation.
|
||
</p>
|
||
|