4.2.3 Mastering Service
Category Android Basic Tutorial
Introduction to This Section:
>
In this section, we will continue to explore the Service component. This section will introduce some concepts of AIDL (Android Interface Definition Language) for inter-process communication in Android, without delving into the source code level. For now, it's enough to understand what it is and how to use it. Let's get started with this section~ The corresponding official documentation: Binder
1. Introduction to Binder Mechanism
1) What are IBinder and Binder?
Let's see what the official documentation says:
>
IBinder is the basic interface for remote objects, designed as the core part of a lightweight remote procedure call mechanism for high performance. It is not only used for remote calls but also for in-process calls. This interface defines the protocol for interacting with remote objects. However, you should not directly implement this interface but extend (extends) Binder instead.
The main API of IBinder is transact(), and the corresponding API is Binder.onTransact(). With the former, you can send calls to a remote IBinder object, and the latter allows your remote object to respond to received calls. The APIs of IBinder are executed synchronously, such as transact() which only returns after the other party's Binder.onTransact() method has been called. When the call occurs within a process, this is undoubtedly the case, and between processes, it achieves the same effect with the help of IPC.
The data sent via transact() is Parcel, which is a general buffer not only containing data but also metadata describing its content. The metadata is used to manage references to IBinder objects, ensuring that when a Parcel moves from one process to another, these references are preserved. This ensures that when an IBinder is written to a Parcel and sent to another process, if that process sends back a reference to the same IBinder, the original process receives the reference to the IBinder it sent. This mechanism makes IBinder and Binder manage references across processes like unique identifiers.
The system maintains a thread pool for each process to dispatch all IPC calls coming from other processes. For example, when an IPC call is sent from process A to process B, the thread in A that made the call (which should not be in the thread pool) blocks on transact(). A thread from the interaction thread pool in process B receives the call, it calls Binder.onTransact(), and after completion, returns a Parcel as the result. Then the waiting thread in process A continues execution upon receiving the returned Parcel. In essence, another process appears as just another thread in the current process, but it is not created by the current process.
The Binder mechanism also supports recursive calls between processes. For example, process A executes its IBinder's transact() to call process B's Binder, and process B, in its Binder.onTransact(), makes another transact() call back to process A. Process A, while waiting for its call to return, will also respond to process B's transact() with Binder.onTransact(). The result of Binder is that it makes cross-process calls feel no different from in-process calls.
When operating on remote objects, you often need to check if they are valid. There are three ways to do this:
- 1 transact() method will throw a RemoteException if the process holding the IBinder does not exist.
- 2 pingBinder() returns false if the target process does not exist.
- 3 You can register an IBinder.DeathRecipient with the IBinder using linkToDeath(), which gets called when the process represented by the IBinder exits.
PS: The Chinese translation is excerpted from: Android Development: What is IBinder
Well, you might be feeling a bit overwhelmed after reading all that. Here's a simple summary:
IBinder is an interface provided by Android for inter-process communication, and we usually do not directly implement this interface, but instead inherit from the Binder class to achieve inter-process communication. It is a way to implement IPC (inter-process communication) in Android!
2) Brief Analysis of Binder Mechanism
>
The Binder mechanism in Android consists of several system components: Client, Server, Service Manager, and Binder driver
The general call flow is as follows, although Service Manager is complex and not detailed here!
Flow Analysis:
>
-> When Client calls a method in a proxy interface, the proxy interface method packages the parameters passed by Client into a Parcel object; -> Then the proxy interface sends this Parcel object to the Binder driver in the kernel; -> The Server then reads the request data from the Binder driver, and if it is intended for itself, unpacks the Parcel object, processes it, and returns the result; AIDL (Android Interface Definition Language)!
3) Why does Android use the Binder mechanism for inter-process communication?
>
Reliability: On mobile devices, a Client-Server based communication method is typically used for internal communication between the internet and the device. Currently, Linux supports IPC including traditional pipes, System V IPC (message queues/shared memory/semaphores), and sockets, of which only sockets support the Client-Server communication method. Android provides developers with a rich set of IPC functionality interfaces, such as media playback, sensors, wireless transmission, all managed by different servers. Developers only need to establish communication between their application's client and the server to use the service. Obviously, setting up a protocol at the lower level to achieve Client-Server communication would increase system complexity. Implementing such a complex environment on a resource-limited mobile device is unreliable.
Transmission Performance: Sockets are mainly used for inter-process communication across networks and on the same machine, but they have low transmission efficiency and high overhead. Message queues and pipes use a store-and-forward method, i.e., data is first copied from the sender's buffer to a buffer in the kernel, then from the kernel buffer to the receiver's buffer, involving at least two copies. Although shared memory does not require copying, it is complex to control. Comparing the number of data copies for various IPC methods: Shared memory: 0 times. Binder: 1 time. Socket/pipe/message queue: 2 times.
Security: Android is an open platform, so ensuring application security is crucial. Android assigns a UID/PID to each installed application, with the process's UID being used to identify the process. Traditional methods can only have the user fill in the UID/PID in the data packet, which is unreliable and can be exploited by malicious programs. We require the kernel to add a reliable UID. Therefore, for reliability, transmission performance, and security, Android has established a new inter-process communication method. — Excerpted from: Brief Understanding of Binder Mechanism in Android
Of course, as a novice developer, we don't care about the above. The most direct benefit that the Binder mechanism brings us is: We don't need to care about how the underlying implementation works, just follow the rules of AIDL, define an interface file, and then call the methods in the interface to complete inter-process communication!
2. Detailed Explanation of AIDL Usage
1) What is AIDL?
>
Hey, earlier we mentioned the term IPC, which stands for inter-process communication. In Android, each application runs in its own process, and processes generally cannot exchange data directly. To achieve inter-process communication, Android provides us with the Binder mechanism mentioned above, and the interface language used by this mechanism is: AIDL (Android Interface Definition Language). Its syntax is simple, and this interface language is not a true programming language but merely defines an interface for communication between two processes. The code that conforms to the communication protocol is generated by the aidl.exe tool in the platform-tools directory of the Android SDK, and the corresponding interface file is generated in the gen directory, typically an: Xxx.java interface! In this interface, there is an internal class called Stub, which implements the IBinder interface and the custom communication interface. This class will serve as the callback class for the remote Service—implementing the IBinder interface, so it can be the return value of the Service's onBind() method!
2) Simple Inter-process Communication with AIDL
Before starting to write the AIDL interface file, we need to be aware of some注意事项:
AIDL注意事项:
>
- The interface name needs to be the same as the aidl file name.
- Do not add access permission modifiers such as public, private, protected, etc., to the interface and methods, nor use static final!
- AIDL natively supports types including Java primitive types, String, List, Map, CharSequence, and any other types need to be imported. For using custom types as parameters or return values, the custom types need to implement the Parcelable interface. See the section on passing complex data types for details.
- Custom types and other AIDL-generated interface types in the aidl description file should be explicitly imported, even if they are in the same package as the class being defined.
Additionally, if you are using Eclipse to write aidl, note: Do not create a new file directly and then establish it! This way, the file cannot be opened and cannot be written!
If you are using Android Studio, unlike Eclipse, if you create an AIDL file as you would in Eclipse, you will find that the corresponding XXX.java file is not compiled and generated. In AS, you need to create an aidl folder in the main directory, then define a package with the same name as the aidl package, and finally create an aidl file. Then press ctrl + f9 to recompile.
The successful compilation results for both are as follows, and you can find the corresponding AIDL files in the respective directories.
1. Server Side:
Step 1: Create an AIDL file:
IPerson.aidl
package com.jay.aidl;
interface IPerson {
String queryPerson(int num);
}
Let's open IPerson.java to see the code inside:
IPerson.java
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Code\\ASCode\\AIDLServer\\app\\src\\main\\aidl\\com\\jay\\aidl\\IPerson.aidl
*/
package com.jay.aidl;
public interface IPerson extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jay.aidl.IPerson
{
private static final java.lang.String DESCRIPTOR = "com.jay.aidl.IPerson";
/** Construct the stub at attach it to the interface. */
public Stub()
{
This is an Chinese to English translation, please provide the English translation for this text. Do not provide any explanations or text apart from the translation. Chinese: this.attachInterface(this, DESCRIPTOR); } /**
- Cast an IBinder object into an com.jay.aidl.IPerson interface,
- generating a proxy if needed. */ public static com.jay.aidl.IPerson asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.jay.aidl.IPerson))) { return ((com.jay.aidl.IPerson)iin); } return new com.jay.aidl.IPerson.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_queryPerson: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); java.lang.String _result = this.queryPerson(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.jay.aidl.IPerson { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.lang.String queryPerson(int num) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(num); mRemote.transact(Stub.TRANSACTION_queryPerson, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_queryPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String queryPerson(int num) throws android.os.RemoteException; }
Here we are only focusing on the **asInterface(IBinder)** method and the **queryPerson()** method defined in our interface!
This method converts an object of type IBinder to type IPerson, and generates a proxy object if necessary to return the result!
We can skip the rest and move on to the next step.
**Step 2:** Customize our Service class, complete the following operations:
1) Inherit the Service class, and also define a PersonQueryBinder class to **inherit the IPerson.Stub class**, which **implements the IPerson interface and the IBinder interface**
2) Instantiate the custom Stub class, and override the onBind method of Service to return a binder object!
**AIDLService.java**
package com.jay.aidlserver;
import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import com.jay.aidl.IPerson.Stub;
/**
- Created by Jay on 2015/8/18 0018. */ public class AIDLService extends Service {
private IBinder binder = new PersonQueryBinder();
private String[] names = {"B神","艹神","基神","J神","翔神"};
private String query(int num)
{
if(num > 0 && num < 6){
return names[num - 1];
}
return null;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private final class PersonQueryBinder extends Stub{
@Override
public String queryPerson(int num) throws RemoteException {
return query(num);
}
}
}
**Step 3:** Register the Service in the AndroidManifest.xml file
<service android:name=".AIDLService"> <intent-filter> <action android:name="android.intent.action.AIDLService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
Here we do not provide an Activity interface, but the application provides a Service that can be called by other apps!
---
**2. Client** **Implement the ServiceConnection interface** **bindService(service, conn, BIND_AUTO_CREATE);** **The ServiceConnection for binding the remote Service cannot directly obtain the onBind() method of the Service** **onBind()** method returns a **proxy object**, which needs to be handled as follows: **iPerson = IPerson.Stub.asInterface(service);**
The specific code is as follows:
**MainActivity.java**
package com.jay.aidlclient;
import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView;
import com.jay.aidl.IPerson;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private EditText edit_num;
private Button btn_query;
private TextView txt_name;
private IPerson iPerson;
private PersonConnection conn = new PersonConnection();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();
// Bind the remote Service
Intent service = new Intent("android.intent.action.AIDLService");
service.setPackage("com.jay.aidlserver");
bindService(service, conn, BIND_AUTO_CREATE);
btn_query.setOnClickListener(this);
}
private void bindViews() {
edit_num = (EditText) findViewById(R.id.edit_num);
btn_query = (Button) findViewById(R.id.btn_query);
txt_name = (TextView) findViewById(R.id.txt_name);
}
@Override
public void onClick(View v) {
String number = edit_num.getText().toString(); int num = Integer.valueOf(number); try { txt_name.setText(iPerson.queryPerson(num)); } catch (RemoteException e) { e.printStackTrace(); } edit_num.setText(""); }
private final class PersonConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { iPerson = IPerson.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName name) { iPerson = null; } } }
Next, start the AIDLServivce first, then start the AIDLClient, and input the query number to get the corresponding name!
Of course, you can also directly start the AIDLClient, and you will get the same effect:
**Effect diagram as follows:**
```java
public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() {
// Read data from Parcel and return Salary object
@Override
public Salary createFromParcel(Parcel source) {
return new Salary(source.readString(), source.readInt());
}
@Override
public Salary[] newArray(int size) {
return new Salary[size];
}
};
public String toString() {
return "Job: " + type + " Salary: " + salary;
}
Step 3: Create an ISalary.aidl file and write a simple method to get salary information:
package com.jay.example.aidl;
import com.jay.example.aidl.Salary;
import com.jay.example.aidl.Person;
interface ISalary {
// Define a Person object as an input parameter
// When defining methods in the interface, specify the parameter passing mode, here it is input, so there is an 'in'
Salary getMsg(in Person owner);
}
Note: Remember to import custom data types if used!!!
Step 4: Write the core Service: Define a SalaryBinder class that extends Stub, thereby implementing the ISalary and IBinder interfaces; define a Map collection to store information! Override the onBind method to return an instance of the SalaryBinder class!
AidlService.java
package com.jay.example.aidl_complexservice;
import java.util.HashMap;
import java.util.Map;
import com.jay.example.aidl.ISalary.Stub;
import com.jay.example.aidl.Person;
import com.jay.example.aidl.Salary;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class AidlService extends Service {
private SalaryBinder salaryBinder;
private static Map<Person, Salary> ss = new HashMap<Person, Salary>();
// Initialize the Map collection, this is done in a static block, but you can also do it in the constructor
static {
ss.put(new Person(1, "Jay"), new Salary("Coder", 2000));
ss.put(new Person(2, "GEM"), new Salary("Singer", 20000));
ss.put(new Person(3, "XM"), new Salary("Student", 20));
ss.put(new Person(4, "MrWang"), new Salary("Teacher", 2000));
}
@Override
public void onCreate() {
super.onCreate();
salaryBinder = new SalaryBinder();
}
@Override
public IBinder onBind(Intent intent) {
return salaryBinder;
}
// Extends Stub, thus implementing both ISalary and IBinder interfaces
public class SalaryBinder extends Stub {
@Override
public Salary getMsg(Person owner) throws RemoteException {
return ss.get(owner);
}
}
@Override
public void onDestroy() {
System.out.println("Service ended!");
super.onDestroy();
}
}
Register the Service:
<service android:name=".AidlService">
<intent-filter>
<action android:name="android.intent.action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
2 — Client Writing
Step 1: Copy the AIDL files from the server, the directory after copying is as follows:
Step 2: Write a simple layout, then implement the core MainActivity: Define a ServiceConnection object, override the corresponding methods, similar to the previous ordinary data Then bindService, and then get the Salary object and display it in the Button's click event!
MainActivity.java
package com.jay.example.aidl_complexclient;
import com.jay.example.aidl.ISalary;
import com.jay.example.aidl.Person;
import com.jay.example.aidl.Salary;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
private ISalary salaryService;
private Button btnquery;
private EditText editname;
private TextView textshow;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
salaryService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// Returns the proxy object, you need to call this method!
salaryService = ISalary.Stub.asInterface(service);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnquery = (Button) findViewById(R.id.btnquery);
editname = (EditText) findViewById(R.id.editname);
textshow = (TextView) findViewById(R.id.textshow);
Intent it = new Intent();
it.setAction("com.jay.aidl.AIDL_SERVICE");
bindService(it, conn, Service.BIND_AUTO_CREATE);
btnquery.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
String name = editname.getText().toString();
Salary salary = salaryService.getMsg(new Person(1, name));
textshow.setText(name + salary.toString());
try {
// Code that might throw a RemoteException
} catch (RemoteException e) {
e.printStackTrace();
}
Screenshot of Execution:
- 1.2.2 Developing Android Apps with Android Studio
- 1.3 Solving SDK Update Issues
- 1.4 Installing Genymotion Emulator
- 1.5.1 Git Tutorial: Basic Operations on Local Repositories
- 1.5.2 Using GitHub to Set Up a Remote Repository with Git
- 1.6 How to Use the 9(Nine Sisters) Image
- 1.7 Interface Prototype Design
- 1.8 Project Analysis (Various Files, Resource Access)
- 1.9 Signing and Packaging Android Applications
- 1.11 Decompiling APK to Retrieve Code & Resources
- 2.1 Concepts of View and ViewGroup
- 2.2.1 LinearLayout (Linear Layout)
- 2.2.2 RelativeLayout (Relative Layout)
- 2.2.3 TableLayout (Table Layout)
- 2.2.4 FrameLayout (Frame Layout)
- 2.2.5 GridLayout (Grid Layout)
- 2.2.6 AbsoluteLayout (Absolute Layout)
- 2.3.1 Detailed Guide to TextView (Text Field)
- 2.3.2 Detailed Guide to EditText (Input Field)
- 2.3.3 Button and ImageButton
- 2.3.4 ImageView (Image View)
- 2.3.5 RadioButton (Radio Button) & Checkbox (Checkbox)
- 2.3.6 ToggleButton and Switch
- 2.3.7 ProgressBar (Progress Bar)
- 2.3.8 SeekBar (Drag Bar)
- 2.3.9 RatingBar (Star Rating Bar)
- 2.4.1 ScrollView (Scroll Bar)
- 2.4.2 Date & Time Components (Part 1)
- 2.4.3 Date & Time Components (Part 2)
- 2.4.4 Adapter Basics
- 2.4.5 Simple Usage of ListView
- 2.4.6 Optimization of BaseAdapter
- 2.4.7 Focus Issues with ListView
- 2.4.8 Solving Checkbox Misalignment in ListView
- 2.4.9 Data Update Issues in ListView
- 2.5.0 Building a Reusable Custom BaseAdapter
- 2.5.1 Implementing Multiple Item Layouts in ListView
- 2.5.2 Basic Usage of GridView (Grid View)
- 2.5.3 Basic Usage of Spinner (Dropdown List)
- 2.5.4 Basic Usage of AutoCompleteTextView (Auto-complete Text Field)
- 2.5.5 Basic Usage of ExpandableListView (Collapsible List)
- 2.5.6 Basic Usage of ViewFlipper (Flip View)
- 2.5.7 Basic Usage of Toast
- 2.5.8 Detailed Guide to Notification (Status Bar Notification)
- 2.5.9 Detailed Guide to AlertDialog (Dialog Box)
- 2.6.0 Basic Usage of Other Common Dialogs
- 2.6.1 Basic Usage of PopupWindow (Floating Box)
- 2.6.2 Menu (Menu)
- 2.6.3 Simple Usage of ViewPager
- 2.6.4 Simple Usage of DrawerLayout (Official Side Sliding Menu)
- 3.1.1 Event Handling Mechanism Based on Listeners
- 3.2 Event Handling Mechanism Based on Callbacks
- 3.3 Analysis of Handler Message Passing Mechanism
- 3.4 TouchListener vs OnTouchEvent + Multi-touch
- 3.5 Listening for Content Changes in EditText
- 3.6 Responding to System Setting Changes (Configuration Class)
- 3.7 AsyncTask Asynchronous Tasks
- 3.8 Gestures (Gestures)
- 4.1.1 Getting Started with Activity
- 4.1.2 Intermediate Activity
- 4.1.3 Advanced Activity
- 4.2.1 Introduction to Service
- 4.2.2 Advanced Service
- 4.2.3 Expert Service
- 4.3.1 Getting Started with BroadcastReceiver
- 4.3.2 In-depth BroadcastReceiver
- 4.4.1 Introduction to ContentProvider
- 4.4.2 Advanced ContentProvider – Document Provider
- 4.5.1 Basic Usage of Intent
- 4.5.2 Passing Complex Data with Intent
- 5.1 Basic Overview of Fragment
- 5.2.1 Fragment Example: Implementing Bottom Navigation Bar (Method 1)
- 5.2.2 Fragment Example: Implementing Bottom Navigation Bar (Method 2)
- 5.2.3 Fragment Example: Implementing Bottom Navigation Bar (Method 3)
- 5.2.4 Fragment Example: Bottom Navigation Bar + ViewPager for Swiping Pages
- 5.2.5 Fragment Example: Simple Implementation of News/Shopping App List Fragment
- 6.1 Data Storage and Access – File Storage and Reading
- 6.2 Data Storage and Access – Saving User Preferences with SharedPreferences
- 6.3.1 Data Storage and Access - Introduction to SQLite Database
- 6.3.2 Data Storage and Access - Another Look at SQLite Database
- 7.1.1 Android Network Programming Essentials and Learning HTTP Protocol
- 7.1.2 Learning Android HTTP Request Headers and Response Headers
- 7.1.3 Android HTTP Request Methods: HttpURLConnection
- 7.1.4 Android HTTP Request Methods: HttpClient
- 7.2.1 Android XML Data Parsing
- 7.2.2 Android JSON Data Parsing
- 7.3.1 Android File Upload
- 7.3.2 Android File Download (1)
- 7.3.3 Android File Download (2)
- 7.4 Android Calling WebService
- 7.5.1 WebView (Web View) Basic Usage
- 7.5.2 WebView and JavaScript Interaction Basics
- 7.5.3 Important Considerations for WebView After Android 4.4
- 7.5.4 WebView File Download
- 7.5.5 WebView Cache Issues
- 7.5.6 WebView Handling Webpage Error Code Information
- 7.6.1 Socket Learning Network Basics Preparation
- 7.6.2 Socket Communication Based on TCP Protocol (1)
- 7.6.3 Socket Communication Based on TCP Protocol (2)
- 7.6.4 Socket Communication Based on UDP Protocol
- 8.1.1 Summary of 13 Drawable Types in Android Part 1
- 8.1.2 Summary of 13 Drawable Types in Android Part 2
- 8.1.3 Summary of 13 Drawable Types in Android Part 3
- 8.2.1 Bitmap (Bitmap) Comprehensive Analysis Part 1
- 8.2.2 OOM Issues Caused by Bitmap
- 8.3.1 Detailed Explanation of Three Drawing Tool Classes
- 8.3.2 Practical Examples of Drawing Classes
- 8.3.3 Paint API - MaskFilter (Mask)
- 8.3.4 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 1)
- 8.3.5 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 2)
- 8.3.6 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 3)
- 8.3.7 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 4)
- 8.3.8 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 5)
- 8.3.9 Paint API - ColorFilter (Color Filter) (1/3)
- 8.3.10 Paint API - ColorFilter (Color Filter) (2/3)
- 8.3.11 Paint API - ColorFilter (Color Filter) (3/3)
- 8.3.12 Paint API - PathEffect (Path Effect)
- 8.3.13 Paint API - Shader (Image Rendering)
- 8.3.14 Paint Enum/Constant Values and ShadowLayer Shadow Effects
- 8.3.15 Paint API - Typeface (Font Style)
- 8.3.16 Canvas API Detailed Explanation (Part 1)
- 8.3.17 Canvas API Detailed Explanation (Part 2) Clipping Methods Collection
- 8.3.18 Canvas API Detailed Explanation (Part 3) Matrix and drawBitmapMesh
- 8.4.1 Android Animation Collection - Frame Animation
- 8.4.2 Android Animation Collection - Tween Animation
- 8.4.3 Android Animation Collection - Property Animation - Introduction
- 8.4.4 Android Animation Collection - Property Animation - Another Look
- 9.1 Using SoundPool to Play Sound Effects (Duang~)
- 9.2 MediaPlayer Playing Audio and Video
- 9.3 Using Camera to Take Photos
- 9.4 Using MediaRecord to Record Audio
- 10.1 TelephonyManager (Telephony Manager)
- 10.2 SmsManager (SMS Manager)
- 10.3 AudioManager (Audio Manager)
- 10.4 Vibrator (Vibrator)
- 10.5 AlarmManager (Alarm Service)
- 10.6 PowerManager (Power Service)
- 10.7 WindowManager (Window Management Service)
- 10.8 LayoutInflater (Layout Service)
- 10.9 WallpaperManager (Wallpaper Manager)
- 10.10 Sensor Topics (1) - Introduction
- 10.11 Sensor Topics (2) - Orientation Sensor
- 10.12 Sensor Topics (3) - Accelerometer/Gyroscope Sensor
- 10.12 Sensor Topics (4) - Understanding Other Sensors
- 10.14 Android GPS Introduction
- 11.0 "2015 Latest Android Basic Beginner's Tutorial" Completion Celebration~
- 12.1 Android Practice: DrySister Look at Girls App (Version 1) - Project Setup and Simple Implementation
- 12.2 DrySister Look at Girls App (Version 1) - Parsing Backend Data
- 12.3 DrySister Look at Girls App (Version 1) - Image Loading Optimization (Writing a Small Image Cache Framework)
- 12.4 DrySister Look at Girls App (Version 1) - Adding Data Caching (Introducing SQLite)
- 12.5 DrySister Look at Girls App (Version 1) - Code Review, Adjustment, and Logging Class Writing
- 12.6 DrySister Look at Girls App (Version 1) - Icon Creation, Obfuscation, Signing, APK Slimming, App Release