12.5 DrySister View Girls App (Version 1) – 5. Code Review, Adjustments, and Logging Class Writing
Category Android Basic Introduction Tutorial
1. Some Chit-Chat
I must admit, it's been over a year since the last DrySister article, and many friends have privately messaged me saying it was great and asking when the next one would be. I mostly replied that it was discontinued... The reasons vary, but recently, with some free time, I decided to wrap up the first version. When I started writing, it was with AS 2.1.2, and now it's AS 3.0.1. The content for this section is as follows:
-Step 1: Review what was covered in the previous sections
-Step 2: Make adjustments to run the code on AS 3.0.1
-Step 3: Write a logging class
Next, we'll talk about signing, obfuscation, and publishing to the app market. No more nonsense, let's get started with this section!
2. Code Review
-Section 1: Project Setup and Simple Implementation
- Git-related operations
- Simple image loading class (fetching network stream -> converting to image -> Handler updates UI)
-Section 2: Parsing Backend Data
- Using Android's built-in JSON parser to parse backend returned JSON data (JSON string -> List<**Bean**>)
- Using AsyncTask for asynchronous network requests
-Section 3: Image Loading Optimization (Writing a Small Image Caching Framework)
- Basic routines for image caching
- Using sampling compression to compress Bitmap, avoiding OOM
- Thread pool management for multiple image loading threads
- Handler updates UI
- String (URL here) to MD5 conversion
- Usage of memory caching class LruCache
- Usage of disk caching class DiskLruCache
- Overall logic design for asynchronous image loading and caching
-Section 4: Adding Data Caching (Introducing SQLite)
- Network status judgment
- Custom SQLiteOpenHelper to create databases
- Database-related operations: add, delete, update, query, transactions, pagination, etc.
These are the contents of the first four sections, which apply important knowledge points in Android basic introduction. If you have mastered them, you can be considered to have barely entered the door of Android, but the road ahead is still long!
3. Code Adjustments
After switching to AS 3.0, the code in the following files needs to be changed. As usual, switch branches with the command: git checkout -b fix, and then start modifying the code:
Project-level build.gradle:
APP-level build.gradle:
gradle-wrapper.properties:
There's also a small change to be made: SisterApi.java, change 福利 to: %e7%a6%8f%e5%88%a9. This involves Chinese encoding issues, and HttpUrlConnection cannot open links containing Chinese characters. You need to encode the Chinese part using URLEncoder.encode(Chinese part, "utf-8");, remember only the Chinese part, not the entire URL!! Encoding also requires catching exceptions. Since there's only one place to encode, I used an online encoding tool directly: https://c.tutorialpro.org/front-end/695
The converted result:
Additionally, there are some minor changes. For buildToolsVersion 26 and above, findViewById does not need to be strongly typed. Click into the method to see that it uses generics, so remove all findViewById:
Finally, I felt that the effect on my Meizu E2 was a bit off, so I adjusted the page layout slightly to make the image width full screen and the height adaptive. Here, the attribute android:adjustViewBounds = "true" is used, which scales width and height proportionally; also, move the text for the next step into the strings.xml file and adjust the margins slightly:
Finally, let's look at the project's running results: (Wow, beautiful girls are indeed pleasing to the eye):
Then merge this branch into the develop branch. Here, we don't use the previous merge routine but use rebase to merge. The specific process is as follows:
git add .
git commit -m "fix code in as3.0"
git checkout develop # Switch to develop branch
git rebase fix_code_as3.0.1 # Merge branch
git push origin develop # Push develop to remote branch
git branch -d fix_code_as3.0.1 # Delete the merged local branch
If you don't understand Git, you can learn from another article. I won't explain it here: Pig's Summary of Git Usage
4. Writing Logging Utility Class and Crash Log Collection Class
Logging logs is something every developer is familiar with and is indispensable for debugging. When the application is packaged for testing, if the tester reports a crash, the first thing we think of is to ask for the logs. Speaking of logging, many developers like to随手一个Log.e(xxx,xxx), treating all logs as Error level, mostly because: red is more noticeable, haha! Then directly print the value of the variable, or add it in a method to verify if the method is executed, etc. Remember to delete it when officially releasing, or it's简直是作死. Anyway, I was scolded by a senior at my previous company, and it left a deep impression on me! Log management is very important. The two utility classes we need to write are as follows:
>
- Logs are printed normally during debug, but not during release
- Crash log collection, testing by yourself or testers is fine. If the app crashes, just connect the phone to your computer and check logcat to see everything clearly. But if the app is installed on a user's phone and crashes, the user won't send you the logs. Multiple crashes may even lead to the user uninstalling your app. So we need to save the logs when the app crashes and upload them to our server when the user connects to wifi or opens the app again. We're just writing this for fun, so we only do local crash log collection. Usually, we integrate third-party statistical tools like Umeng, Bugly, etc., for log collection.
Okay, the requirements are the above two points. Next, we prepare to start writing the code. But before writing the code, let's talk about two points regarding Log, which most of you may already know:
1) Quickly Print Log
Open Settings, click through: Live Templates -> AndroidLog and check all the log printing options. You can also write your own templates in the Template text below~
Then type log... in any code and press enter, and the Log statement will appear, with the TAG being the current method name~
2. About Log Usage
1) Quickly Print Log Commands
Open Settings, click through: Live Templates -> AndroidLog and check all the log printing options. You can also write your own templates in the Template text below~
Then type log... in any code and press enter, and the Log statement will appear~
If you are outside a method, typing logt can directly generate a TAG with the class name:
2) Log Level Knowledge
My former team leader once mentioned our bad habit of directly using Log.e during a small meeting. Different Log levels should print different information:
>
Log.v: Verbose (冗长) Detailed information during development and debugging, should not be compiled into the product, used only during development
Log.d: Debug (调试) Information used for debugging, compiled into the product, turned off during runtime.
The following three levels should not be used as ordinary debugging information. Using these levels indiscriminately can cause unnecessary trouble for developers when analyzing bugs.
Log.i: Info (信息) Status information during runtime, which can provide help when issues arise.
Log.w: Warning (警告) Warns that the application has encountered an exception, but may not immediately result in an error, needs attention
Log.e: Error (错误) The application has encountered an error, the most critical to focus on and resolve!
3) Writing the Log Utility Class
As usual, create a branch: bug. This is very simple. Print during debug, do not print during release, using BuildConfig.Debug for judgment. The code is as follows:
4) Writing the Crash Log Collection Class
The crash log collection class relies on Application and Thread.UncaughtExceptionHandler to implement~ When the application is about to terminate due to an uncaught exception, it will use Thread.UncaughtExceptionHandler to query the thread of UncaughtExceptionHandler, call uncaughtException method, and pass the thread and exception as parameters. If the thread does not explicitly set an UncaughtExceptionHandler, it will use its ThreadGroup as its UncaughtExceptionHandler, and then throw it to the default uncaught exception handler. So we just need to implement the UncaughtExceptionHandler interface, override the uncaughtException method, to implement our custom handling. Let's go through the logic list first:
- Create a folder specifically for log files: need to check if the storage card is available, then check if the folder exists, if not, create a new folder;
- Need a method to write strings to a file
- Content composition of crash logs: current time, application version, device information, crash log
- Get the system default UncaughtException handler, then check if it is null, if not, set it to the custom UncaughtException, here we use a singleton
- Finally, restart the application, set to restart the application after 1s;
The general logic is as above, let's go through it step by step. First, steps 1 and 2:
Then the crash log, composed of several parts: first, the current time
Then the application version and device information, here we use a HashMap to store:
Then the exception information, this is simple, just pass the exception object, call printStackTrace directly. Finally, combine them together:
The writing to file is also solved, then the custom UncaughtExceptionHandler singleton and the default UncaughtExceptionHandler handler's acquisition, set to the custom UncaughtExceptionHandler, also need to override the UncaughtException method
Then we write our own exception handling routine into a method. When an exception occurs, pop up a Toast to inform the user that the application needs to restart, and call the method to write the log:
Then the overridden UncaughtException method makes a judgment, whether the exception was handled by us, and whether the default UncaughtExceptionHandler is null, i.e., was the exception handled? If not handled, throw it to the custom UncaughtExceptionHandler, if handled, restart the application.
Finally, add the relevant code for restarting the application, and it's done:
At this point, our crash log collection tool class is complete. To enable it, you need to add it in the onCreate() method of DrySisterApp.java:
If you want to test if it works, it's simple, just manually cause a crash. For example, I do a division by zero operation in the next step button:
After running the application and clicking the next step, it crashes directly. Open the root directory of the built-in storage to see if there is a Crash folder. If you open it and see our log file, it means success:
At this point, it's done, merge the branch to develop, and then push it to the remote repository:
git add .
git commit -m "add LogUtils and CrashHandler"
git checkout develop # Switch to develop branch
git rebase bug_log_catch # Merge branch
git push origin develop # Push develop to remote branch
git branch -d bug_log_catch # Delete the merged local branch
5. Summary
This section first reviewed the previously written code, then made minor adjustments due to the switch to AS 3.0, and finally wrote a logging utility class and a crash log collection utility class. Although it's just a small image display program, it covers most introductory knowledge. The next section will be the conclusion of the first version, including signing and packaging, obfuscation, and publishing to the CoolMarket! Stay tuned~
Code Download:
https://github.com/coder-pig/DrySister/tree/develop Feel free to follow, star, and if you have any suggestions, please submit issues!
- 1.0 Android Basic Introductory Tutorial
- 1.0.1 2015 Latest Android Basic Introductory Tutorial Contents
- 1.1 Background and System Architecture Analysis
- 1.2 Development Environment Setup
- 1.2.1 Developing Android APP with Eclipse + ADT + SDK
- 1.2.2 Developing Android APP with Android Studio
- 1.3 Solving SDK Update Issues
- 1.4 Genymotion Emulator Installation
- 1.5.1 Git Tutorial for Basic Local Repository Operations
- 1.5.2 Git: Setting Up a Remote Repository on GitHub
- 1.6 How to Use the 9-Patch Image
- 1.7 Interface Prototype Design
- 1.8 Project Source Analysis (Various Files, Resource Access)
- 1.9 Android Program Signing and Packaging
- 1.11 Decompiling APK to Obtain 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 Explanation of TextView (Text Box)
- 2.3.2 Detailed Explanation of EditText (Input Box)
- 2.3.3 Button (Button) and ImageButton (Image Button)
- 2.3.4 ImageView (Image View)
- 2.3.5 RadioButton (Radio Button) & Checkbox (Checkbox)
- 2.3.6 ToggleButton (Toggle Button) and Switch (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 in 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 Multi-Layout ListView Items
- 2.5.2 Basic Usage of GridView (Grid View)
- 2.5.3 Basic Usage of Spinner (List Option Box)
- 2.5.4 Basic Usage of AutoCompleteTextView (Auto-Complete Text Box)
- 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 Explanation of Notification (Status Bar Notification)
- 2.5.9 Detailed Explanation of 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 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 to EditText Content Changes
- 3.6 Responding to System Setting Events (Configuration Class)
- 3.7 AsyncTask Asynchronous Task
- 3.8 Gestures (Gestures)
- 4.1.1 Activity Beginner
- 4.1.2 Activity Intermediate
- 4.1.3 Activity Advanced
- 4.2.1 Service Beginner
- 4.2.2 Service Intermediate
- 4.2.3 Service Advanced
- 4.3.1 BroadcastReceiver Beginner
- 4.3.2 BroadcastReceiver Advanced
- 4.4.1 ContentProvider Beginner
- 4.4.2 ContentProvider Intermediate - Document Provider
- 4.5.1 Basic Usage of Intent
- 4.5.2 Passing Complex Data with Intent
- 5.1 Overview of Fragment Basics
- 5.2.1 Fragment Example Walkthrough - Bottom Navigation Bar Implementation (Method 1)
- 5.2.2 Fragment Example Walkthrough - Bottom Navigation Bar Implementation (Method 2)
- 5.2.3 Fragment Example Walkthrough - Bottom Navigation Bar Implementation (Method 3)
- 5.2.4 Fragment Example Walkthrough - Bottom Navigation Bar + ViewPager for Swipe Page Switching
- 5.2.5 Fragment Example Walkthrough - Simple Implementation of News (Shopping) App List Fragment
- 6.1 Data Storage and Access - File Storage and Reading/Writing
- 6.2 Data Storage and Access - SharedPreferences for Saving User Preferences
- 6.3.1 Data Storage and Access - Introduction to SQLite Database
- 6.3.2 Data Storage and Access - Further Exploration of 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 Basic Usage of WebView (Web View)
- 7.5.2 Basic Interaction Between WebView and JavaScript
- 7.5.3 Important Considerations for WebView in Android 4.4 and Later
- 7.5.4 WebView File Download
- 7.5.5 WebView Cache Issues
- 7.5.6 WebView Handling Error Code Information from Web Pages
- 7.6.1 Network Basics Preparation for Socket Learning
- 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 Comprehensive Analysis of Bitmap (Bitmaps) 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 Detailed Explanation of Canvas API (Part 1)
- 8.3.17 Detailed Explanation of Canvas API (Part 2) Clipping Methods Collection
- 8.3.18 Detailed Explanation of Canvas API (Part 3) Matrix and drawBitmapMesh
- 8.4.1 Frame Animation in Android Animation Collection
- 8.4.2 Tween Animation in Android Animation Collection
- 8.4.3 Property Animation in Android Animation Collection - Introduction
- 8.4.4 Property Animation in Android Animation Collection - Further Exploration
- 9.1 Playing Sound Effects with SoundPool (Duang~)
- 9.2 MediaPlayer for Audio and Video Playback
- 9.3 Using Camera for Photography
- 9.4 Using MediaRecord for Audio Recording
- 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 Special Topic (4) – Understanding Other Sensors
11.0 Completion of the Latest Android Basic Beginner's Tutorial 2015
12.2 DrySister's App for Viewing Girls (Version 1) – 2. Parsing Backend Data
12.4 DrySister's App for Viewing Girls (Version 1) – 4. Adding Data Caching (Incorporating SQLite)
12.5 DrySister's App for Viewing Girls (Version 1) – 5. Code Review, Adjustment, and Logging Class Writing