Hot Article Guide | Click the title to read
How to advance to become a Java and Android architect?
Alibaba technical experts guide you to quickly build Web engineering projects using the Spring framework
Kotlin may replace Java – Author of “Thinking in Java” Bruce Eckel
Source: http://www.apkbus.com/blog-955387-77980.html (Source code address at the end of the article)
Previously shared Android Optimization Series – Code, Images, and Layout Optimization, today we continue to share the network and power optimization part.
Network Optimization. Most mobile apps are connected to the internet, and network latency can have a significant impact on app performance. Network optimization can save network traffic and power.
-
The DNS domain system mainly functions by mapping the IP address corresponding to the domain URL used by the application request in the network mapping table. This process can consume hundreds of milliseconds and may have the risk of DNS hijacking. It can be replaced by connecting directly to the IP address instead of accessing the domain, achieving faster network requests. However, using IP addresses is not flexible enough; if the backend changes the IP address, access may fail. The front-end app needs to send packets. The solution is to enhance the capability of dynamic IP address updates or switch to domain access when the IP address fails.
Demo —> ping an address, if incorrect, switch to a backup address
boolean ping = ping("wwww.baidu.com");
/**
* Test if the main domain name is available
*
* @param ip
* @return
*/
private final int PING_TIME_OUT = 1000; // ping timeout
private boolean ping(String ip) {
try {
Integer status = executeCommandIp( ip, PING_TIME_OUT );
if ( status != null && status == 0 ) {
return true;
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return false;
}
/**
* Execute command to check if the domain is accessible
* @param command
* @param timeout
* @return
* @throws IOException
* @throws InterruptedException
* @throws TimeoutException
*/
private int executeCommandIp( final String command, final long timeout )
throws IOException, InterruptedException, TimeoutException {
Process process = Runtime.getRuntime().exec(
"ping -c 1 -w 100 " + command);
mWorker = new PingWorker(process);
mWorker.start();
try {
mWorker.join(timeout);
if (mWorker.exit != null) {
return mWorker.exit;
} else {
return -1;
}
} catch (InterruptedException ex) {
mWorker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
process.destroy();
}
}
PingWorker Class
class PingWorker extends Thread {
private final Process process;
private Integer exit;
private String ip;
public PingWorker(Process process) {
this.process = process;
}
@Override
public void run() {
try {
exit = process.waitFor();
if (exit == 0) {
BufferedReader buf = new BufferedReader(new InputStreamReader(process.getInputStream()));
String str = new String();
StringBuffer ipInfo = new StringBuffer();
while((str=buf.readLine())!=null) {
ipInfo.append(str);
}
System.out.println("shiming ipInfo----->"+ipInfo);
Pattern mPattern = Pattern.compile("\((.*?)\)");
Matcher matcher = mPattern.matcher(ipInfo.toString());
if ( matcher.find() ) {
ip = matcher.group( 1 );
}
}
else {
ip = " process.waitFor()==="+exit;
}
}
catch (IOException e) {
e.printStackTrace();
ip="java.io.IOException: Stream closed";
return;
}
catch (InterruptedException e) {
ip="java.io.InterruptedException: Stream closed";
return;
}
}
}
-
Combine network requests. A complete HTTP request first performs a DNS lookup, establishes a connection through a TCP three-way handshake, and if it is an HTTPS request, it also needs to complete a TLS handshake successfully before connecting. For network requests, reduce interfaces and combine network requests whenever possible.
-
SSL (Secure Sockets Layer) and its successor Transport Layer Security (TLS) are security protocols that provide security and data integrity for network communications. TLS encrypts network connections at the transport layer.
The main differences between HTTPS and HTTP are as follows:
-
1. The HTTPS protocol requires applying for a certificate from a CA, and generally, free certificates are rare, and fees are required.
-
2. HTTP is the Hypertext Transfer Protocol, and information is transmitted in plaintext, while HTTPS is a secure SSL encrypted transmission protocol.
-
3. HTTP and HTTPS use completely different connection methods and different ports; the former uses port 80, while the latter uses port 443.
-
4. The connection of HTTP is simple and stateless; the HTTPS protocol is a network protocol built by SSL + HTTP that can perform encrypted transmission and identity authentication, making it more secure than the HTTP protocol.
-
Pre-fetching data can concentrate network requests at once, allowing the phone to switch to idle time during other periods, thus avoiding frequent wake-ups and saving power.
-
Avoid polling: If a network request needs to be actively initiated to the server every period, it is not recommended to do this on the app side; push notifications can be used. If unavoidable, avoid using the Thread.sleep() function for looping; it is recommended to use the system’s AlarmManager to implement timed polling, which can ensure that the CPU can rest while the system is asleep and wake up only when the next network request needs to be initiated.
-
Avoid unlimited looping retries when network requests fail. In my first blog post on Jianshu, I mentioned a network loading framework: MVP network framework (Retrofit + RxJava + RxAndroid).
// Based on RxJava and RxAndroid Retrofit
o.subscribeOn(Schedulers.io())
.retryWhen(new RetryWhenHandler(1, 5))
.doOnSubscribe(new Action0() {
@Override
public void call() {
s.onBegin();
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s);
-
Offline caching for images or files, memory cache + disk cache + network cache. Generally, we need to do a two-level cache locally. When the cache contains images or files, read directly from the cache without going online. To download images, use LruCache for memory caching and DiskLruCache for local caching in Android.
/**
* Core class for image caching
*/
private LruCache<String, Bitmap> mLruCache;
// Cache size
private static final int CACHE_MAX_SIZE = 1024;
/**
* LRU (Least Recently Used) algorithm eliminates data based on historical access records, with the core idea being "if data has been accessed recently, it is more likely to be accessed in the future".
*/
private void lruCacheDemo() {
// Get the maximum available memory for the application
int maxMemory = (int) Runtime.getRuntime().maxMemory();
// Set the size of LruCache, usually 1/8 of the current process's available capacity.
int cacheSize = maxMemory / 8;
mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue != newValue) {
oldValue.recycle();
}
}
};
}
/**
* Get an image from LruCache; if it does not exist, return null.
*/
private Bitmap getBitmapFromLruCache(String key) {
return mLruCache.get(key);
}
/**
* Add an image to LruCache
* @param key
* @param bitmap
*/
private void addBitmapToLruCache(String key, Bitmap bitmap) {
if (getBitmapFromLruCache(key) == null) {
if (bitmap != null)
mLruCache.put(key, bitmap);
}
}
-
Compressing the size of data: You can perform gzip compression on the data sent to the server and use a better data transmission format, such as binary instead of JSON format. This is quite advanced and probably rarely used; using webp format instead of image formats.
-
Use different timeout strategies for different network environments. Common network formats include 2G, 3G, 4G, and Wi-Fi. Update the current network status in real-time by listening to obtain the latest network type and dynamically adjust the network timeout.
private void netWorkDemo() {
TextView netWork = findViewById(R.id.net_work);
boolean networkConnected = NetworkUtils.isNetworkConnected(this);
int networkType = NetworkUtils.getNetworkType(this);
System.out.println("shiming 是否联网了"+networkConnected);
switch (networkType){
case TYPE_UNKNOWN:
System.out.println("shiming 联网的类型---无网络连接");
netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---无网络连接");
break;
case TYPE_2G:
System.out.println("shiming 联网的类型---2G");
netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---2G");
break;
case TYPE_3G:
System.out.println("shiming 联网的类型---TYPE_3G");
netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---TYPE_3G");
break;
case TYPE_4G:
System.out.println("shiming 联网的类型---TYPE_4G");
netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---TYPE_4G");
break;
case TYPE_WIFI:
System.out.println("shiming 联网的类型---TYPE_WIFI");
netWork.setText("是否联网了---》"+networkConnected+" 联网的类型---TYPE_WIFI");
break;
}
}
NetworkUtils class
package com.shiming.performanceoptimization.network_optimization;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
/**
* author: Created by shiming on 2018/4/28 10:52
* mailbox:[email protected]
* des:Network connection utility class
*/
public class NetworkUtils {
private static final String SUBTYPE_TD_SCDMA = "SCDMA";
private static final String SUBTYPE_WCDMA = "WCDMA";
private static final String SUBTYPE_CDMA2000 = "CDMA2000";
/**
* Check if connected to the network.
* @param context Context
* @return Whether connected to the network
*/
public static boolean isNetworkConnected(Context context) {
ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context
.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo info = connectivity.getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
return false;
}
/**
* Get the current network type
* @param context Context
* @return Current network type (Unknown, 2G, 3G, 4G, WIFI)
*/
public static int getNetworkType(Context context) {
NetworkInfo info = ((ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getType() == ConnectivityManager.TYPE_WIFI) {
return NetworkType.TYPE_WIFI;
} else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
switch (info.getSubtype()) {
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_1xRTT:
case TelephonyManager.NETWORK_TYPE_IDEN:
return NetworkType.TYPE_2G;
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_EVDO_0:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
case TelephonyManager.NETWORK_TYPE_EHRPD:
case TelephonyManager.NETWORK_TYPE_HSPAP:
return NetworkType.TYPE_3G;
case TelephonyManager.NETWORK_TYPE_LTE:
return NetworkType.TYPE_4G;
default:
String subtypeName = info.getSubtypeName();
if (SUBTYPE_TD_SCDMA.equalsIgnoreCase(subtypeName) ||
SUBTYPE_WCDMA.equalsIgnoreCase(subtypeName) ||
SUBTYPE_CDMA2000.equalsIgnoreCase(subtypeName)) {
return NetworkType.TYPE_3G;
} else {
return NetworkType.TYPE_UNKNOWN;
}
}
}
}
return NetworkType.TYPE_UNKNOWN;
}
}
NetworkType class
package com.shiming.performanceoptimization.network_optimization;
/**
* author: Created by shiming on 2018/4/28 10:52
* mailbox:[email protected]
* des:Network connection type constants
*/
public class NetworkType {
/**
* No network connection
*/
public static final int TYPE_UNKNOWN = -1;
/**
* 2G
*/
public static final int TYPE_2G = 0;
/**
* 3G
*/
public static final int TYPE_3G = 1;
/**
* 4G
*/
public static final int TYPE_4G = 2;
/**
* WIFI
*/
public static final int TYPE_WIFI = 3;
}
-
CDN stands for Content Delivery Network. Its basic idea is to avoid bottlenecks and links on the internet that may affect data transmission speed and stability, allowing content to be transmitted faster and more stably. By placing node servers throughout the network, the CDN system can redirect user requests to the service node closest to the user based on comprehensive information such as network traffic, connection status, load conditions of each node, and distance and response time to users. The goal is to allow users to obtain the required content nearby, alleviate Internet congestion, and improve response speed when accessing websites.
Power Optimization
-
1. (BroadcastReceiver) To reduce power consumption of the application, we should avoid using unnecessary operational code in our code. When the application goes to the background, refreshing all pages is meaningless and wastes power. For example, if there is a broadcast that listens to the network status and performs some actions, such as pop-ups or Toasts, then this function should be disabled when the app is in the background.
-
2. Data transmission: Bluetooth transmission, Wi-Fi transmission, mobile network transmission. Background data management: avoid transmitting invalid data as much as possible according to business needs. Data transmission frequency: determine the transmission frequency based on empirical values or statistical methods to avoid redundant and repetitive data transmission. During data transmission, compress the size of the data, combine network requests, and avoid polling.
-
3. Location services: Correct use of location resetting is key to application power consumption.
Note the following three points:
1. Whether to timely unregister the location listener: similar to broadcasts.
2. Setting the frequency of location update listeners: set a reasonable update frequency based on business needs.
-
minTime: used to specify the minimum time interval for update notifications, in milliseconds; here it is updated every 1 second according to the logs.
-
minDistance: used to specify the minimum distance for location update notifications, in meters.
3. Android provides three types of positioning:
-
GPS positioning: achieves positioning through GPS, with the highest accuracy, usually within 10 meters (Mars coordinates), but GPS positioning consumes the most time and power.
-
Network positioning: calculates the phone’s location based on the differences in mobile communication base station signals, which is much less accurate than GPS.
-
Passive positioning: the most power-saving positioning service. If using passive positioning service, it means it wants to know location update information but does not actively obtain it. It waits for other applications, services, or system components in the phone to issue location requests and receives location information along with the listeners of these components. In actual development, third-party maps such as Amap, Tencent, and Baidu are generally used, which have done a good job of encapsulation and optimization in terms of performance on the map.
/**
* //Set positioning accuracy Criteria.ACCURACY_COARSE is relatively rough, Criteria.ACCURACY_FINE is more precise.
* criteria.setAccuracy(Criteria.ACCURACY_FINE);
* //Set whether speed is required
* criteria.setSpeedRequired(false);
* // Set whether to allow operators to charge
* criteria.setCostAllowed(false);
* //Set whether to require bearing information
* criteria.setBearingRequired(false);
* //Set whether to require altitude information
* criteria.setAltitudeRequired(false);
* // Set power requirements
* criteria.setPowerRequirement(Criteria.POWER_LOW);
*/
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_LOW);
String serviceName = Context.LOCATION_SERVICE;
mLocationManager = (LocationManager) getSystemService(serviceName);
// locationManager.setTestProviderEnabled("gps", true);
// TODO: 2018/5/3 IllegalArgumentException 'Provider "gps" unknown" https://www.cnblogs.com/ok-lanyan/archive/2011/10/12/2208378.html
mLocationManager.addTestProvider(LocationManager.GPS_PROVIDER,
"requiresNetwork" == "", "requiresSatellite" == "", "requiresCell" == "", "hasMonetaryCost" == "",
"supportsAltitude" == "", "supportsSpeed" == "",
"supportsBearing" == "", android.location.Criteria.POWER_LOW,
android.location.Criteria.ACCURACY_FINE);
mProvider = mLocationManager.getBestProvider(criteria, true);
//Get latitude
//Get longitude
mLlistener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (location != null) {
double lat = location.getLatitude();
double lng = location.getLongitude();
System.out.println("shiming lat+" + lat);
System.out.println("shiming lng+" + lng);
String name = Thread.currentThread().getName();
mCount++;
System.out.println("当前线程的位置name---"+name+"i==="+mCount);
mTv_location.setText("位置信息是2s变动的,可以设置,我是第"+mCount+"次变动的--->\n
"+"lat===="+lat+" lng----->"+lng);
}
if (mLocationManager!=null) {
mLocationManager.removeUpdates(this);
if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 2000, 1000, mLlistener);
}else {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 1000, mLlistener);
}
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
};
/**
* minTime: used to specify the minimum time interval for update notifications, in milliseconds; here it is updated every 1 second according to the logs.
* minDistance: used to specify the minimum distance for location update notifications, in meters.
*/
mLocationManager.requestLocationUpdates(mProvider, 2000, (float) 1000.0, mLlistener);
In OnDestroy, manually set variables to null. During my testing, the location listener only stops when the value is null. Interested partners can take a good look at what operations are performed when the value is null.
/**
* Remember to destroy this listener,
* todo If not manually set to null, you can find through logs that this listener is still running, so manually setting it to null is beneficial.
*/
protected void onDestroy() {
if (mAm!=null){
mAm.cancel(mPi);
mAm=null;
}
if (null!=mTag){
mTag.release();
mTag=null;
}
mLocationManager.removeUpdates(mLlistener);
if (mLocationManager != null) {
mLocationManager.removeUpdates(mLlistener);
mLocationManager = null;
}
if (mLlistener != null) {
mLlistener = null;
}
super.onDestroy();
}
-
WakeLock is an API to keep the device awake, preventing it from entering sleep mode for a long time. For example, when a user is watching a movie. When using WakeLock, it is necessary to release the lock in a timely manner. For example, when playing a video, WakeLock keeps the screen on, and when paused, it should release the lock instead of waiting until playback stops.
@SuppressLint("WakelockTimeout")
private void wakeLockDemo() {
PowerManager powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
mTag = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Tag");
if (null!= mTag){
mTag.acquire();
}
}
-
AlarmManager is also relatively power-consuming. Usually, ensure that the time interval between two wake-up operations is not too short. If wake-up functionality is not needed, cancel it as soon as possible; otherwise, the application will continue to consume power. AlarmManager is a system-level service provided by the SDK that can broadcast a specified Intent at a specific time. This pendingIntent can be used to start an Activity, Service, or BroadcastReceiver, and the app will start in the background.
private void alarmManager() {
Intent intent = new Intent("action");
intent.putExtra("msg","Restart App ---Le --- Go back to the foreground");
mPi = PendingIntent.getBroadcast(this,0,intent,0);
mAm = (AlarmManager)getSystemService(ALARM_SERVICE);
assert mAm != null;
mAm.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),1000, mPi);
}
/**
* author: Created by shiming on 2018/5/3 14:28
* mailbox:[email protected]
*/
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
System.out.println("shiming " + msg) ;
Toast.makeText(context,msg,Toast.LENGTH_SHORT).show();
}
}
Providing a demo, when the app crashes, use AlarmManager to restart the app.
/**
* author: Created by shiming on 2018/5/3 14:28
* mailbox:[email protected]
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
public static CrashHandler mAppCrashHandler;
private Thread.UncaughtExceptionHandler mDefaultHandler;
private MyApplication mAppContext;
public void initCrashHandler(MyApplication application) {
this.mAppContext = application;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
public static CrashHandler getInstance() {
if (mAppCrashHandler == null) {
mAppCrashHandler = new CrashHandler();
}
return mAppCrashHandler;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
ex.printStackTrace();
AlarmManager mgr = (AlarmManager) mAppContext.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(mAppContext, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("crash", true);
PendingIntent restartIntent = PendingIntent.getActivity(mAppContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 5000, restartIntent);
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
}
-
The program will restart. If you click on power optimization, and the app crashes, please grant all permissions and also set location information simulation in developer mode. If it crashes, you will also find that the app will automatically restart, which is an application of AlarmManager.
/**
* author: Created by shiming on 2018/5/3 14:48
* mailbox:[email protected]
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().initCrashHandler(this);
}
}
The above is a basic summary that is not very comprehensive or detailed. If possible, please give a little star to encourage me. Thank you.
Git Address: https://github.com/Shimingli/PerformanceOptimizationForAndroid
If you have good articles to share with everyone, welcome to submit. Just send me the article link directly.
Finally, everyone is welcome to join our knowledge circle, this issue ends on March 10, 2019, so the sooner you join, the better. The ball friends who joined now are nearly 1000 people. When it reaches 1000 people, the price will increase significantly (there are only a few last spots left), so hurry up!
Scan the QR code above or click to receive advanced resources for Android, Python, AI, Java, etc.
For more learning materials, click below “Read the original text” to obtain