In-depth understanding of the impact of the communication mechanism between Android components on Object-Oriented features

Source: Internet
Author: User

Features of components
For the four components of Android, Activity, Service, ContentProvider, and Service, there cannot be Setter and Getter, nor can you add interfaces to the component. The reason is that all components are called to the system framework. developers can only implement the prescribed callback interfaces. the creation and destruction of components are controlled by the system framework, and developers cannot forcibly intervene, there is no way to obtain the component object. For example, Activity, Service, and BroadcastReceiver, you have no way to create an Activity, Service, or BroadcastReceiver, and then call the interfaces on it to communicate with it like other classes, use Setters and Getters to change attributes. This also determines that only Intent supported by the system can be used for communication between components. However, Intent can only pass some common data types, such as basic data types and Uris. Intent only supports passing built-in types and some restricted types. As a result, data transmission between components must be of the basic type, so enumeration types cannot be used.
Polymorphism cannot be achieved
For example, if you have a Service that is used to execute requests in the background UI, some of these requests are data requests, some are data analysis, and so on. Here we can use polymorphism to define a unified Transaction class, and then use process () for each specific Transaction type. Ideally, the Service receives a Transaction object and calls its process (). There is no need to know the specific type. The UI creates a specific type object and submits it to the Service for processing. However, this cannot be implemented in Android because Intent communication mechanism is limited because it cannot directly transmit Transaction objects. Therefore, the Service must know the specific type. This is the case in native application Mms. In the transaction package, TransactionService is a processing Service. What the UI sends to the Service is to distinguish the IDS (an integer) of different transactions ), service to view different IDs and create different Transaction objects, and then call process () to process them.
Suggestion:Implement a Service-like Service by yourself, and use Handler, Thread, and logoff to run it for a long time. In this way, there is no restriction on inter-component communication. You can use this service class as you normally use Java objects to pass custom processing requests to them:

Copy codeThe Code is as follows: public class TransactionServer extends HandlerThread {
Public TransactionServer (){
Start ();
}
Public void onLooperPrepared (){
MHandler = new Handler (getLooper (), new Handler. Callback (){
@ Override
Public void handleMessage (Message msg ){
Transaction request = (Transaction) msg. obj;
Request. process ();
}
}
}

Public void execute (Transaction request ){
If (mHandler = null ){
Return;
}
Message msg = Message. obtain ();
Msg. obj = request;
MHandler. sendMessage (msg );
}
}

You can create the Server object in the Activity and use it:Copy codeThe Code is as follows: TransactionServer server = new TransactionServer ();
Transaction updateRequest = new UpdateTransaction ();
Server.exe cute (updateRequest );

In addition, AIDL can communicate with the Service. Although the Service object reference can be obtained, the Service method can be called directly, but this is also limited. For the AIDL interface, all parameters and return types must be basic data.
Data type. No object exists. The reason is also understandable, because AIDL also needs to pass IPC, even if the Service and Activity are in the same process, it is essentially no different from the Intent communication mechanism.
Encapsulation damaged
The communication mechanism between components determines Android encapsulation. Let's look at some examples:Copy codeThe Code is as follows: Intent I = new Intent (Intent. ACTION_VIEW );
I. setDataAndType (uri, "text/html ");
StartActivity (I );

This is even more common in Android.
The use of Intent and IntentFilter greatly damages encapsulation, because you must directly write strings and parameters to Intent or IntentFilter. For example:Copy codeThe Code is as follows: Intent I = new Intent ("android. contacts. action. MULTIPLECONTACTSLISTS ");
I. setExtra ("request_type", 3 );
<Intent-filter>
<Action android: name = "android. contacts. action. MULTIPLECONTACTSLISTS"/>
</Intent-filter>

Of course, even better:
Intent I = new Intent (Contacts. ACTION_GET_CONTACTS );
However, the IntentFilter in AndroidManifest still needs to write the String constant (Literal Strings), so there is a problem, that is, even if you write an error, the compiler will not remind you, it is not until you run it that the program is not working properly. You debug, debug, and finally find that the string is wrong. Or, if the activity name is wrong, the compiler will not remind you, but RuntimeException is reported because you cannot find the class during running.
Suggestion:Define constants in all scopes as much as possible to ensure consistency between components and interfaces. In particular, for string constants, they must be defined within the scopes visible to both components, otherwise, maintenance will be troublesome.
Example: intent. putExtra ("request_type", 3); --> intent. putExtra (TargetActivity. REQUEST_TYPE, TargetActivity. NO_BACKGROUND); otherwise, especially when the target component is not in the same package or is far away from each other, if the other party changes, there will be no errors during compilation, but the program will not work normally, this causes hard-to-detect bugs.
Unable to pass custom data structures between components
As described above, you cannot set data to a component because you cannot obtain the reference of the component object. Of course, you can use the static method, but it is not elegant and difficult to maintain (for the Service, you can obtain the reference of the Service object through AIDL, and then call its method to add data ). Because Intent can only carry basic data types, it is especially troublesome to transmit custom data structures between components. Of course, you can implement the Parcleable interface for the data structure, but it is quite troublesome to use.
Suggestion:
1. Avoid using custom data structures as much as possible, especially data structures with no behavior except Setters and Getters.
For structured data, define ContentProvider for it and write the data to the SQLite database. In this way, each row of data in the database table is equivalent to a data object, and each column is its attribute. Because Android components are highly viscous with the SQLite database, each component can easily obtain data from the database, and then use Cursor and other tools to operate the data. The most important thing is that it is very convenient to transmit data between components. The access factor of ContentProvider is implemented through Uri, and Uri can be seamlessly connected with Intent, uri can be easily put into and retrieved from Intent, and each component can directly access ContentProvider to read data with Uri, so that seamless data transmission between components can be realized.
2. Try not to transmit data between components
Do not use too many activities, and the Service can also be free of charge. The Activity + thread may solve most of the problems. Of course, the thread is not so easy to use, especially in Android.
3. Avoid passing custom data structures between components
As mentioned above, it is best to directly transfer the basic data and the data types supported by Intent between components. For a custom data structure, either do not define the data structure or do not pass between components, otherwise it will be very troublesome, although it may implement the Parcelable interface, however, the efficiency and ease of operation are greatly reduced.
Enumeration and Integer Set
Previous Article<Android development Note: How to Use Enum (Enumeration type) to replace an integer set>I once said that we should try to use enum instead of the Integer Set (ints), and many programming books (objective Java) also suggest using enumeration instead of the Integer Set. The advantage of this is to reduce the error rate, run-Time check can be placed at compile time. Because the Integer Range is large, you can pass any integer until it is detected or has problems, however, enumeration checks the type during compilation. If it is not a valid enumeration, the compiler will complain.
However, we can see that the situation in Android is poor. Android uses a large number of integer sets, and the system defines a large number of integer sets. Many parameters are also integers, although the correct method is to pass the defined Integer constant to these APIs, if you pass an Integer. MAX_VALUE or Integer. MIN_VALUE, at least during compilation.
Since this is not a good programming specification, why do Android need to use a large number of integer sets? The reason is that components communicate with each other and parameters must be passed between components, but Intent can only be placed into the basic data type. That is to say, if enumeration is used, Intent cannot be passed to other components, it is easy to convert an enumeration to an integer, but it is not so easy to convert an integer to an enumeration.
Therefore, if your constant does not need to be passed back and forth between components, it is best to define it as enumeration. Otherwise, you can only use an integer set.
General Design Principles of components
1. Do not use components to implement certain interfaces, such as click interfaces.
Because a component is a very costly object and has a very deep hierarchy of inheritance, it is equivalent to using a train to deliver a mouse to the caller through the component implementation interface, it gives others a very large object, but only one or two methods are needed by others. Especially for Activity, do not implement some public interfaces such as View. onClickListener, in addition to the previous reasons, is that your onClick must use conditions to distinguish which UI element is clicked, which is difficult to maintain, another reason is that the Activity object is not very stable, because some system events, such as screen conversion and language switching, will kill the Activity and re-create an instance, which may cause problems, although it seems that the Activity is still there, it is not the same instance. If something is related to a specific instance, it will cause a problem, either the program is not working properly or there is a RuntimeException. Memory leakage may also occur because the interface objects sent to the user are all instance references of the Activity. Once a reference exceeds the lifecycle of the Activity, the memory leakage may occur.
We recommend that you use an anonymous internal class to implement the interface. If you need to perform operations on this interface object elsewhere, you can declare a member variable or an internal class to facilitate Activity control, to ensure that everything is within the lifecycle of the Activity.
2. Fewer services
The component Service is not so useful as it is, and it will let your program exit the page and still run in the background, occupying system resources and getting scolded (see these articles ), because the lifecycle of a Service is controlled by the system, we cannot intervene, even if you know exactly that you have completely no longer used it. You can use the Activity and thread to complete the vast majority of operations, and you can also make all threads within the control of the Activity, so that they all live within the lifecycle of the Activity. The other reason is that because threads belong to self-built classes or common Java classes, object-oriented applications can be applied because there is no restrictions on component communication.
3. Use ContentProvider for communication tools with complex data structures
ContentProvider and SQLiteDatabase store structured data, which is equivalent to a data structure. Its reference is its Uri,Any component can obtain this data structure through Uri. It has the following advantages:
1. easy transfer between components
Because the data is actually in the database, you only need to pass its URL Uri between components. any component or any class holding the Context can easily obtain it, in terms of practicality and efficiency, this is faster than using Intent to transmit data structure objects.
2. The ContentProvider component has its own processes and threads, so there will be no thread synchronization problems.
Externally, ContentResolver is used to access ContentProvider. Therefore, ContentProvider is the same for the outside world. If the access method is the same, there will naturally be no issues such as thread synchronization.
3. ContentProvider can be encapsulated to facilitate data operations
Because ContentProvier provides a unified interface, you can use the characteristics of data to encapsulate these interfaces, such as adding default values.
4. ContentProvider can be used as a queue or stack
Because each row is a structured data, and the data insertion order of each row is sequential, this can be regarded as a queue or a stack.

You can refer to the message sending process in native Mms. The information is written to the database after the user clicks send, and its Uri is transmitted among various components all the way. Each component updates the information status, until the last message is sent. DownloadProvider, which is the default download in Android. The application submits a Request through DownloadManager, but DownloadService is used for download, while DownloadServer is in packages/provider/DownloadProvider, is a completely independent process. DownloadManager only writes a Request to DownloadProvider. This Request contains information about downloading an object, such as a URL. DownloadService only listens for DownloadProvider changes. Once new data is inserted, a thread is created to read the Request and start downloading. Data is directly updated to the DownloadProvider during the download, so that the UI can display the progress and other information. This process involves two processes, at least three components: the user process that submits the Request and the DownloadProvider process, DownloadManager (a public API), DownloadService (separate process, private package) and DownloadList (in the DownloadProvider package, the UI used to display the download progress), there is no direct communication between these components, they are all around ContentProvider. At the same time, the ContentProvider here is also used as the download Request queue. DownloadManager can continuously add requests to it, and DownloadService will listen to its changes to retrieve data from it and then download it.
Not to mention the ease of Android Development
Although Android is easy to use, it is not easy to write high-quality code. Splitting, fragmentation, and system architecture all increase the difficulty of many things. You can see that the main Activity code in the native application is more than 5000 lines. Their interfaces are complex and are the core business logic. These activities control a lot of things, so it is very bloated. Of course, the main reason is that it still fails to be well designed and reconstructed. For example, the Browser in ICS is better. Its BrowserActivity only has several hundred lines of code, but the previous code is more than 6000 lines. Now it separates various business logics, activity is only responsible for receiving callbacks at the Frameworks layer. All business logic control is implemented by the Controller, while the Controller is only responsible for Tab management and menu management. The specific menu and layout resolution are handled by PhoneUi. DownloadHandler processes the download, and so on. It turns out that all these things are put in BrowserActivity. We can imagine how messy the logic in it would be and how painful it would be to maintain it. Of course, the current design still needs to be improved, because the coupling between classes is still very large. For example, the Controller holds the PhoneUi object, but the PhoneUi object holds the Controller, and so on. In many cases, mutual calling may occur, which is quite difficult to maintain and undermines many design principles.

In short, if you want to write a program well, you need to contribute additional energy. Although the platform has advantages and disadvantages, it is more important to invest in code. However, sadly, the profit on the Android platform is not satisfactory, and the fragmentation and impetuous mentality make many applications come out within one or two months, therefore, the application quality in the entire Android ecosystem is not high. What's more serious is the decompilation and cloning. Many people just capture the application, decompile it, and change it to a new application, the more you don't pay attention to quality, the less you buy, and the more profitable the developer is, the harder it is to put effort into application. This leads to a vicious circle.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.